package cc.rengu.igas.cops.core.service.trans.payment;

import cc.rengu.igas.cops.common.constant.CopsParamConstant;
import cc.rengu.igas.cops.common.enums.RespCodeEnum;
import cc.rengu.igas.cops.common.enums.SignTokenTypeEnum;
import cc.rengu.igas.cops.core.service.base.ManageService;
import cc.rengu.igas.cops.facade.request.AuthenticationRequest;
import cc.rengu.igas.cops.facade.response.AuthenticationResponse;
import cc.rengu.igas.route.facade.bean.ProdInfo;
import cc.rengu.igas.route.facade.response.SmartRouteResponse;
import cc.rengu.igas.route.facade.result.Result;
import cc.rengu.igas.share.common.constant.ShareParamConstant;
import cc.rengu.igas.share.common.constant.TransCacheConstant;
import cc.rengu.igas.share.common.constant.UserSessionConstant;
import cc.rengu.igas.share.common.enums.SignTypeEnum;
import cc.rengu.igas.share.core.model.PlatMchntInfo;
import cc.rengu.igas.share.core.model.UserInfo;
import cc.rengu.igas.share.core.realize.MchntService;
import cc.rengu.igas.share.core.realize.UserService;
import cc.rengu.igas.share.core.realize.impl.MchntServiceImpl;
import cc.rengu.igas.share.core.realize.impl.UserServiceImpl;
import cc.rengu.oltp.service.base.ChannelBaseService;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.dao.DstChannelCfgMapper;
import cc.rengu.oltp.service.common.dao.SignAcctInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.DstChannelCfgMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SignAcctInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.common.enums.ChannelEnum;
import cc.rengu.oltp.service.common.enums.CheckResultEnum;
import cc.rengu.oltp.service.common.enums.TransStatusEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.model.OrderInfo;
import cc.rengu.oltp.service.realize.BinInfoService;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.TransCustomImpl;
import cc.rengu.oltp.service.realize.impl.BinInfoServiceImpl;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.ConvertUtil;
import cc.rengu.oltp.utility.util.GlobalTransUtil;
import cc.rengu.oltp.utility.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class CopsAuthenticationService extends ManageService implements TransCustomImpl {
    @Override
    protected Object bizBeforeCust() throws Exception {
        AuthenticationRequest authenticationRequest = new AuthenticationRequest();
        ConvertUtil.convertOutput(authenticationRequest);
        return authenticationRequest;
    }

    @Override
    protected BizResponse bizProcessCust(Object request) throws Exception {
        AuthenticationRequest authenticationRequest = (AuthenticationRequest) request;
        AuthenticationResponse authenticationResponse = new AuthenticationResponse();
        authenticationResponse.setHeader(authenticationRequest.getHeader());
        String instId = authenticationRequest.getHeader().getInstId();
        String userId = authenticationRequest.getUserId();
        String payAcctNo = authenticationRequest.getPayAcctNo();
        String certType = authenticationRequest.getCertType();
        String certNo = authenticationRequest.getCertNo();

        //用户所属主体
        String userInst = instId;
        if (null != platMchntInfo) {
            if (SignTypeEnum.PLAT_CLASS_SIGN.getSignTypeCode().equals(platMchntInfo.getSignType())) {
                userInst = platMchntInfo.getPlatMchntNo();
            }
        }

        //获取用户信息
        UserService userService = new UserServiceImpl();
        UserInfo userInfo = userService.getUserInfo(instId, userId, authenticationRequest.getHeader().getSession());
        if (null == userInfo) {
            rglog.debug("用户未登录情况下跳转三要素验证，先根据证件信息获取用户信息!");
            authenticationResponse.setUserLogin(AppParamConstant.NO);
            userInfo = userService.getUserInfoByCertInfo(instId, userInst, certType, certNo);
            if (null != userInfo && null != platMchntInfo && "1".equals(platMchntInfo.getGrantCreditFlag())) {
                //授信平台，上送的用户标识与证件信息查询出来的用户不一致时，拒绝交易
                if (!userId.equals(userInfo.getUserId())) {
                    rglog.error("授信平台存在用户，但上传的用户:<{}>与平台的用户不一致:<{}>", authenticationRequest.getUserId(), userInfo.getUserId());
                    throw new BizException(RespCodeEnum.USER_INFO_MISMATCH.getRespCode(), RespCodeEnum.USER_INFO_MISMATCH.getRespDesc());
                }
            }
        } else {
            //已登录用户校验证件号是否正确
            authenticationResponse.setUserLogin(AppParamConstant.YES);
            if (userInfo.getCertType().equals(certType) && !userInfo.getCertNo().equals(certNo)) {
                rglog.error("上送证件类型证件号不一致,上送证件类型:<{}>,上送证件号:<{}>,用户保存的证件号:<{}>", certType, certNo, userInfo.getCertNo());
                throw new BizException(RespCodeEnum.USER_INFO_MISMATCH.getRespCode(), RespCodeEnum.USER_INFO_MISMATCH.getRespDesc());
            }
        }
        authenticationResponse.setUserExist(null == userInfo ? AppParamConstant.NO : AppParamConstant.YES);

        //校验卡BIN
        BinInfoService binInfoService = new BinInfoServiceImpl();
        BinInfo cardBinInfo = binInfoService.getCardBinInfo(payAcctNo);
        if (null == cardBinInfo) {
            rglog.info("获取卡BIN信息为空,cardNo:<{}>", payAcctNo);
            throw new BizException(RespCodeEnum.GET_CARD_BIN_INFO_ERROR.getRespCode(), RespCodeEnum.GET_CARD_BIN_INFO_ERROR.getRespDesc());
        }
        GlobalTransUtil.setCacheValue(TransCacheConstant.CARD_BIN_INFO, cardBinInfo);
        authenticationResponse.setIssrInstId(cardBinInfo.getIssuerCode());
        authenticationResponse.setIssrName(cardBinInfo.getIssuerName());
        authenticationResponse.setCardType(cardBinInfo.getCardType());

        //判断本他行
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam corporationOrgCodeParam = sysParamService.getSysParamInfo(instId, CopsParamConstant.OPMS_SYS_PARAM, CopsParamConstant.CORPORATION_ORG_CODE);
        if (null == corporationOrgCodeParam || AppParamConstant.NO.equals(corporationOrgCodeParam.getParamStatus())) {
            rglog.info("法人行机构编码未配置或配置失效,instId:<{}>", instId);
            throw new BizException(RespCodeEnum.SYSTEM_ERROR.getRespCode(), RespCodeEnum.SYSTEM_ERROR.getRespDesc());
        }
        String[] split = corporationOrgCodeParam.getParamValue().split(",");
        List<String> corporationorgList = Arrays.asList(split);
        rglog.debug("系统参数配置的银联机构号:<{}>，当前卡发卡机构号:<{}>", JSON.toJSONString(corporationorgList), cardBinInfo.getIssuerCode().substring(0, 4));
        if (corporationorgList.contains(cardBinInfo.getIssuerCode().substring(0, 4))) {
            rglog.info("当前三要素验证卡号为本行卡,签约通道默认为本行快捷!");
            authenticationResponse.setSignChannel(SignTokenTypeEnum.BANK_QUICK_PAY.getSignTypeCode());
        } else {
            rglog.info("当前三要素验证卡号为他行卡,通过路由查询获取最优签约通道!");
            if (null == txnInfo.getChannelCallCfgList() || txnInfo.getChannelCallCfgList().isEmpty()) {
                rglog.error("<{}-{}>交易未配置交易通道服务调用配置信息！", txnInfo.getSrcTxnAuthCfg().getTxnNum(), txnInfo.getSrcTxnAuthCfg().getTxnName());
                throw new BizException(RespCodeEnum.CHANNEL_CONFIG_NOT_EXIST.getRespCode(), RespCodeEnum.CHANNEL_CONFIG_NOT_EXIST.getRespDesc());
            }
            List<ChannelCallCfg> channelCallCfgList = txnInfo.getChannelCallCfgList().stream().filter(item ->
                    item.getCallIndex().equals(ChannelBaseService.FIRST_CHANNEL_CALL_INDEX)).collect(Collectors.toList());
            ChannelCallCfg channelCallCfg = channelCallSceneDiscern(authenticationRequest, channelCallCfgList);
            //路由查询获取最优通道
            Class channelClasz = Class.forName(channelCallCfg.getCallChannelImpl());
            ChannelBaseService channelBaseService = (ChannelBaseService) channelClasz.newInstance();
            channelBaseService.callService(txnInfo, null, channelCallCfg.getBizScene(), request);
            String signTunnel = (String) GlobalTransUtil.getCacheValue(TransCacheConstant.SIGN_CHANNEL_ID);
            if (StringUtil.isEmptyOrNull(signTunnel)) {
                rglog.info("当前不存在可以使用的签约通道");
                throw new BizException(RespCodeEnum.SIGN_CHANNEL_USELESS.getRespCode(), RespCodeEnum.SIGN_CHANNEL_USELESS.getRespDesc());
            }
            authenticationResponse.setSignChannel(signTunnel);
        }

        //获取机构默认的限额配置
        SysParam singleSysParam = sysParamService.getSysParamInfo(instId, CopsParamConstant.OPMS_SYS_PARAM, CopsParamConstant.SINGLE_LIMIT_AMT);
        authenticationResponse.setSingleLimit(null == singleSysParam ? "" : singleSysParam.getParamValue());
        SysParam daySysParam = sysParamService.getSysParamInfo(instId, CopsParamConstant.OPMS_SYS_PARAM, CopsParamConstant.DAY_LIMIT_AMT);
        authenticationResponse.setDateLimit(null == daySysParam ? "" : daySysParam.getParamValue());
        SysParam noPasswordSysParam = sysParamService.getSysParamInfo(instId, CopsParamConstant.OPMS_SYS_PARAM, CopsParamConstant.NO_PASSWD_LIMIT_AMT);
        authenticationResponse.setNoPasswordAmt(null == noPasswordSysParam ? "" : noPasswordSysParam.getParamValue());

        //判断三要素验证卡号是否已签约
        SignAcctInfoMapper signAcctInfoMapper = new SignAcctInfoMapperImpl();
        List<SignAcctInfo> signAcctInfoList = signAcctInfoMapper.selectSignAcctInfoBySignAcctNo(instId, payAcctNo);
        if (null == signAcctInfoList || signAcctInfoList.isEmpty()) {
            authenticationResponse.setAcctNoIsSign(AppParamConstant.NO);
        } else {
            String signInst = userService.getUserSignInst();
            Optional<SignAcctInfo> signAcctInfoOpt = signAcctInfoList.stream().filter(item -> item.getSignInst().equals(signInst) && AppParamConstant.YES.equals(item.getSignStatus())).findFirst();
            if (signAcctInfoOpt.isPresent()) {
                authenticationResponse.setAcctNoIsSign(AppParamConstant.YES);
            } else {
                authenticationResponse.setAcctNoIsSign(AppParamConstant.NO);
            }
        }
        userService.setUserSessionValue(UserSessionConstant.AUTHENTICATION_FINISH_FLAG, AppParamConstant.YES);

        //组装返回报文
        BizResponse<AuthenticationResponse> bizResponse = new BizResponse<>();
        bizResponse.setResult(authenticationResponse);
        bizResponse.setRspSysId(AppParamConstant.SYS_ID);
        bizResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
        bizResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        return bizResponse;
    }

    @Override
    protected void bizAfterCust(Object response) throws Exception {
        BizResponse bizResponse = (BizResponse) response;
        AuthenticationResponse authenticationResponse = (AuthenticationResponse) bizResponse.getResult();
        ConvertUtil.convertInput(authenticationResponse);
    }

    @Override
    public ChannelCallCfg channelCallSceneDiscern(Object request, List<ChannelCallCfg> channelCallCfgList) throws Exception {
        Optional<ChannelCallCfg> channelCallCfgOpt = channelCallCfgList.stream().filter(item ->
                item.getBizScene().equals(ShareParamConstant.CALL_CFG_DEFAULT_BIZ_SCENE)).findFirst();
        return channelCallCfgOpt.orElse(null);
    }

    @Override
    public CheckResultEnum channelCallPreCheck(Object request, ChannelCallCfg channelCallCfg, OrderInfo orderInfo) throws Exception {
        return CheckResultEnum.PASSED;
    }

    @Override
    public TransStatusEnum channelCallResultProc(Object request, ChannelCallCfg channelCallCfg, TransStatusEnum transStatusEnum, Object channelResponse, OrderInfo needUpdateOrderInfo) throws Exception {
        if (TransStatusEnum.TRANS_SUCC.equals(transStatusEnum)) {
            cc.rengu.igas.route.facade.result.Result<SmartRouteResponse> callResult = (Result<SmartRouteResponse>) channelResponse;
            if (null != callResult && null != callResult.getResult()) {
                SmartRouteResponse smartRouteResponse = callResult.getResult();
                List<ProdInfo> prodList = smartRouteResponse.getProdList();
                List<ProdInfo> collect = prodList.stream().sorted(Comparator.comparing(ProdInfo::getPriority)).collect(Collectors.toList());
                for (ProdInfo prodInfo : collect) {
                    String signTunnel = convertChannel(prodInfo.getDstChannelId(), (AuthenticationRequest) request);
                    if (!StringUtil.isEmptyOrNull(signTunnel)) {
                        GlobalTransUtil.setCacheValue(TransCacheConstant.SIGN_CHANNEL_ID, signTunnel);
                        break;
                    }
                }
            }
        }
        return transStatusEnum;
    }

    /**
     * 通道转换
     *
     * @param srcChannel 智能路由推荐通道
     * @return 返回通道
     */
    private String convertChannel(String srcChannel, AuthenticationRequest request) {
        rglog.info("智能路由推荐通道,channelId:<{}>", srcChannel);
        switch (srcChannel) {
            case "UPAC":
                return checkSupUpacChannel(request);
            case "NCQS":
                return checkSupNcqsChannel(request);
            case "NUCC":
                return "04";
            default:
                return "";
        }
    }

    /**
     * 校验全渠道通道是否可用
     *
     * @param request 请求体
     * @return 通道代码
     */
    private String checkSupUpacChannel(AuthenticationRequest request) {
        rglog.info("推荐全渠道签约，校验全渠道参数是否配置....");
        try {
            MchntService mchntService = new MchntServiceImpl();
            PlatMchntInfo platMchntInfo = mchntService.getPlatMchntInfo(request.getHeader().getInstId(), request.getPlatMchntNo());
            if (null != platMchntInfo && "01".equals(platMchntInfo.getPlatMchntStatus())) {
                rglog.info("商户:<{}>属于平台商户，不校验平台商户是否支持全渠道无跳转通道并直接返回通道标识", request.getPlatMchntNo());
                return SignTokenTypeEnum.UPAC_TOKEN.getSignTypeCode();
            }

            //获取全渠道后台通知地址
            String instId = request.getHeader().getInstId();
            SysParamService sysParamService = new SysParamServiceImpl();
            SysParam sysParamInfo = sysParamService.getSysParamInfo(instId, "upac", CopsParamConstant.UPAC_BANK_URL);
            if (null == sysParamInfo || AppParamConstant.NO.equals(sysParamInfo.getParamStatus())) {
                rglog.info("机构全渠道后台通知地址未配置,instId:<{}>", instId);
                return null;
            }

            //从支付通道信息表获取全渠道的tokenId 和 tokenType
            DstChannelCfgMapper dstChannelCfgMapper = new DstChannelCfgMapperImpl();
            List<DstChannelCfg> dstChannelCfgList = dstChannelCfgMapper.selectDstChannelCfgBydstChannelId(instId, ChannelEnum.UPAC.getChannelType());
            dstChannelCfgList = dstChannelCfgList.stream().filter(item -> !StringUtil.isEmptyOrNull(item.getOtherConfParam())).collect(Collectors.toList());
            if (!dstChannelCfgList.isEmpty()) {
                for (DstChannelCfg dstChannelCfg : dstChannelCfgList) {
                    JSONObject jsonObject = JSONObject.parseObject(dstChannelCfg.getOtherConfParam());
                    String trId = (String) jsonObject.get("trId");
                    String tokenType = (String) jsonObject.get("tokenType");
                    if (StringUtil.isEmptyOrNull(trId) && StringUtil.isEmptyOrNull(tokenType)) {
                        return SignTokenTypeEnum.UPAC_TOKEN.getSignTypeCode();
                    }
                }
            }
        } catch (Exception e) {
            rglog.error("全渠道通道检查异常:<{}>", e);
            return null;
        }
        return null;
    }

    /**
     * 校验无卡快捷通道是否可用
     *
     * @param request 请求体
     * @return 通道代码
     */
    private String checkSupNcqsChannel(AuthenticationRequest request) {
        rglog.info("推荐无卡快捷通道签约，校验全渠道参数是否配置....");
        try {
            //获取机构无卡快捷的机构号
            SysParamService sysParamService = new SysParamServiceImpl();
            SysParam ncqsInstCodeParam = sysParamService.getSysParamInfo(request.getHeader().getInstId(), CopsParamConstant.OPMS_SYS_PARAM, "NCQS_INST_CODE");
            if (null == ncqsInstCodeParam || AppParamConstant.NO.equals(ncqsInstCodeParam.getParamStatus())) {
                rglog.warn("机构为设置无卡快捷银联机构号，instId:<{}>", request.getHeader().getInstId());
                return null;
            }
        } catch (Exception e) {
            rglog.error("判断无卡快捷通道是否支持异常:<{}>", e);
            return null;
        }
        return SignTokenTypeEnum.NCQS_PAY.getSignTypeCode();
    }
}

